无线传输时间同步 (基于NRF52设备) 您所在的位置:网站首页 nrf52840模块 nrf24l01功耗 无线传输时间同步 (基于NRF52设备)

无线传输时间同步 (基于NRF52设备)

2023-11-17 18:10| 来源: 网络整理| 查看: 265

原文地址:https://devzone.nordicsemi.com/nordic/b/blog/posts/wireless-timer-synchronization-among-nrf5-devices

简介:

      有一些情况需要很多设备同步时钟。

     一些无线协议如蓝牙对底层的射频硬件实现了优秀的抽象。这使得顶层的开发者无需关心底层的具体实现。直接调用send函数就可以把数据发到指定的位置,而无需关心环境噪声。

    蓝牙是是可信赖的协议的一个例子。顶层数据应用发送的数据会在底层被重复发送,直到对方返回一个接收成功回应。应用也不需要考虑底层究竟尝试发送了多少次,这是在时间同步中的一个很大的问题。纠正数据可以通过后续发送多次来完成。在本篇文章中,实现了一个更简单(或者说是精确?)的同步方法:在  in a proprietary radio mode下,通过softDevice的时间间隙中准确地发送和接收定时信标信号   ;

概要:

    网络中每个节点都保持一个带有16位计数器的自由运行16 MHz定时器,这意味着定时器将溢出,大约以224Hz的频率循环。定时器可以通过GPIOTE+PPI的方式触发,通过定时器来翻转一个GPIO,然后通过逻辑分析仪来测量这个GPIO将得到以下结果:

 

         这个自由运行的定时器将是同步的基础,网络中的一个节点将作为时间主机,目的是使用该节点的时间来同步其他节点中自由运行的定时器。然后通过示波器或者逻辑分析仪来查看GPIO的输出,可以验证一个同步网络中彼此时间的接近程度。

         时间主机将会按找配置好的间隔发射同步包(主机主动发送??),这些数据包将包含一个值,该值指示无线数据包相对于自由运行的16 MHz定时器的传输时间,当其他节点接收到该同步包时,他们可以使用包里的时间值,加上一个正向或者负向的偏移来更新自己的时间。

     一个信标的例子:

struct { int32_t timer_val; int32_t rtc_val; } sync_pkt;

         取决于想要的精度和功耗,可以使用RTC而非定时器来实现低精确度和低功耗的时间同步,在下面的代码示例中,仅使用16 MHz定时器。

        通常情况下,传输速率越慢,时钟漂移的时间就越长,因此,应权衡好无线传输活动(功率/共存)与准确性的关系。

        注意,因为PPI和定时器都是运行在16M的时钟上,这意味着本次设计的时间基本单位为62.5ns(一个时钟周期)。

一致性

为了实现精确的时间同步保持,保持所有与时序相关的因素尽可能一致是很重要的,包括:

 让定时主机每次发送信标同步包,在定时器捕获和无线电传输之间具有相同的偏移使用最精确的振荡器进行计时,即使用外部的高速和低速晶振,不要使用内部RC振荡器使用硬件定时器和触发器(而不是CPU)进行以下操作:                     自由运行计时器捕获           自由定时器更新           同步包发送           捕获数据包接收的时间

特别是最后一条非常重要(即使用硬件定时器和触发器),如果CPU用于定时器操作和数据包传输触发,还有许多其他因素会引入抖动和偏移:其他高优先级中断,编译器设置,高速缓存未命中(nRF52),内存总线时钟域抖动(nRF52)。

时间间隙(Time slot)

        SoftDevice时隙API允许顶级应用程序请求在BLE活动之间访问无线电硬件。 在该示例中,使用时间间隙使得正常BLE活动可以与时间同步功能同时运行。

        此示例的代码实现以下时隙行为:

        默认情况下,每个节点将请求用于在RX模式下运行无线电的时隙,监听时间信标。 所有可用的无线电时间都将用于此(效率不是很高)。        当按下nRF52-DK上的按钮1时,该设备将充当定时主机并以可配置的间隔(默认为100 Hz)开始发送时间信标。        该代码基于SDK HTS示例,在同步活动期间,设备可通过BLE连接  

       完整代码获取请访问: https://github.com/nordic-auko/nRF5-ble-timesync-demo

       以下为代码片段:

       time_sync.h实现以下API:

typedef struct { uint8_t rf_chn; /** RF Channel [0-80] */ uint8_t rf_addr[5]; /** 5-byte RF address */ uint8_t ppi_chns[3]; /** PPI channels */ uint8_t ppi_chhg; /** PPI Channel Group */ NRF_TIMER_Type * high_freq_timer[2]; /** 16 MHz timer (e.g. NRF_TIMER2) */ NRF_RTC_Type * rtc; } ts_params_t; /**@brief SoftDevice system event handler. Must be called when a system event occurs */ void ts_on_sys_evt(uint32_t sys_evt); /**@brief Initialize time sync library * * @param[in] p_params Parameters * * @retval NRF_SUCCESS if successful */ uint32_t ts_init(const ts_params_t * p_params); /**@brief Enable time sync library. This will enable reception of sync packets. * * @retval NRF_SUCCESS if successful */ uint32_t ts_enable(void); /**@brief Disable time sync library. * * @retval NRF_SUCCESS if successful */ uint32_t ts_disable(void); /**@brief Start sync packet transmission (become timing master). * * @note @ref ts_enable() must be called prior to calling this function * @note Expect some jitter depending on BLE activity. * * @param[in] sync_freq_hz Frequency of transmitted sync packets. * * @retval NRF_SUCCESS if successful */ uint32_t ts_tx_start(uint32_t sync_freq_hz); /**@brief Stop sync packet transmission (become timing slave again). * * @retval NRF_SUCCESS if successful */ uint32_t ts_tx_stop(void);

如 ts_params_t 所示,代码运行需要以下资源:

   3 个PPI 通道   1个PPI组    2个16Mhz的定时器

        其中一个16 MHz定时器是自由运行的定时器,另一个定时器用于准确触发主无线电传输。 请注意,这可以简化为仅使用一个额外的计时器,因为TIMER0可以在时隙内使用来触发无线发送。

        使用的无线电参数非常基本。 请注意,此代码包括使用nRF52改进,更快的无线电加速时间。 除此之外,代码也可以在nRF51上运行(期望相同的结果)(即博主觉得可以在51上运行。。。)。以下为配置参数:

static void update_radio_parameters() { // TX power NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm MODE = RADIO_MODE_MODE_Ble_1Mbit MODECNF0 = RADIO_MODECNF0_RU_Fast CRCCNF = RADIO_CRCCNF_LEN_Two CRCINIT = 0xFFFFUL; // Initial value NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16+x^12^x^5+1 // Packet format NRF_RADIO->PCNF0 = (0 EVENTS_COMPARE[0]; NRF_PPI->CH[ppi_chn].TEP = (uint32_t) &m_params.high_freq_timer[0]->TASKS_CAPTURE[1]; NRF_PPI->CHENSET = (1 CH[ppi_chn2].EEP = (uint32_t) &m_params.high_freq_timer[1]->EVENTS_COMPARE[1]; NRF_PPI->CH[ppi_chn2].TEP = (uint32_t) &NRF_RADIO->TASKS_START; NRF_PPI->CHENSET = (1 PRESCALER = 4; // 1 us resolution m_params.high_freq_timer[1]->MODE = TIMER_MODE_MODE_Timer SHORTS = TIMER_SHORTS_COMPARE1_STOP_Msk | TIMER_SHORTS_COMPARE1_CLEAR_Msk; m_params.high_freq_timer[1]->TASKS_STOP = 1; m_params.high_freq_timer[1]->TASKS_CLEAR = 1; m_params.high_freq_timer[1]->CC[0] = 40; // Matches 40 us radio rampup time m_params.high_freq_timer[1]->CC[1] = 50; // Margin for timer readout m_params.high_freq_timer[1]->EVENTS_COMPARE[0] = 0; m_params.high_freq_timer[1]->EVENTS_COMPARE[1] = 0; NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk; NRF_RADIO->TASKS_TXEN = 1; m_params.high_freq_timer[1]->TASKS_START = 1; while (m_params.high_freq_timer[1]->EVENTS_COMPARE[0] == 0) { // Wait for timer to trigger __NOP(); } m_radio_state = RADIO_STATE_TX; m_sync_pkt.timer_val = m_params.high_freq_timer[0]->CC[1]; m_sync_pkt.rtc_val

         第二个时序关键部分是接收器在收到同步信标数据包时如何更新其本地自由运行定时器。 请注意以下代码中的魔术值“TX_CHAIN_DELAY”:

static inline void sync_timer_offset_compensate(void) { uint32_t chn0, chn1, chg; int32_t peer_timer; int32_t local_timer; int32_t timer_offset; peer_timer = m_sync_pkt.timer_val; peer_timer += TX_CHAIN_DELAY; local_timer = m_params.high_freq_timer[0]->CC[1]; if (local_timer > peer_timer) { timer_offset = TIMER_MAX_VAL - local_timer + peer_timer; } else { timer_offset = peer_timer - local_timer; } if (timer_offset == 0 || timer_offset == TIMER_MAX_VAL) { // Already in sync return; } chn0 = m_params.ppi_chns[0]; chn1 = m_params.ppi_chns[1]; chg = m_params.ppi_chhg; // Use a timer compare register to reset the timer according to the offset value // PPI channel 0: clear timer when offset value is reached NRF_PPI->CHENCLR = (1 CH[chn0].EEP = (uint32_t) &m_params.high_freq_timer[0]->EVENTS_COMPARE[2]; NRF_PPI->CH[chn0].TEP = (uint32_t) &m_params.high_freq_timer[0]->TASKS_CLEAR; // PPI channel 1: disable PPI channel 0 such that the timer is only reset once. NRF_PPI->CHENCLR = (1 CH[chn1].EEP = (uint32_t) &m_params.high_freq_timer[0]->EVENTS_COMPARE[2]; NRF_PPI->CH[chn1].TEP = (uint32_t) &NRF_PPI->TASKS_CHG[chg].DIS; // Use PPI group for PPI channel 0 disabling NRF_PPI->TASKS_CHG[chg].DIS = 1; NRF_PPI->CHG[chg] = (1 CC[2] = (TIMER_MAX_VAL - timer_offset); // Enable PPI channels NRF_PPI->CHENSET = (1


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有